/*
 * Copyright 2007 Agile Plugins Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.googlecode.agileplugins.refactor;

import com.intellij.psi.*;
import com.intellij.util.IncorrectOperationException;

import java.util.HashMap;
import java.util.Map;

public class InlineParameterHelper {
    private PsiClass thisClass;
    private Map currentParameterExpressions;

    public InlineParameterHelper(PsiClass thisClass, PsiParameter[] parameters, PsiExpression[] expressions, int parameterIndex) {
        this.thisClass = thisClass;
        currentParameterExpressions = new HashMap();
        for (int j = 0; j < expressions.length; j++) {
            if (j != parameterIndex && parameters[j] != null) {
                currentParameterExpressions.put(expressions[j].getText(), parameters[j].getName());
            }
        }
    }

    PsiElement process(PsiElement exp) {
        if (exp instanceof PsiExpression) {
            exp = fixUpReferences((PsiExpression) exp);
        }
        PsiElement[] children = exp.getChildren();
        for (int i = 0; i < children.length; i++) {
            PsiElement child = children[i];
            process(child);
        }
        return exp;
    }

    private PsiExpression fixUpReferences(PsiExpression exp) {
        try {
            String paramExpression = (String) currentParameterExpressions.get(exp.getText());
            if (paramExpression != null) {
                PsiExpression expressionFromText = exp.getManager().getElementFactory().createExpressionFromText(paramExpression, exp);
                return (PsiExpression) exp.replace(expressionFromText);
            }
            if (exp instanceof PsiReferenceExpression) {
                PsiReferenceExpression refExp = (PsiReferenceExpression) exp;
                PsiElement psiElement = refExp.resolve();
                if (psiElement instanceof PsiLocalVariable) {
                    PsiLocalVariable localVariable = (PsiLocalVariable) psiElement;
                    PsiExpression initializer = localVariable.getInitializer();
                    if (initializer != null) {
                        return (PsiExpression) exp.replace(process(initializer.copy()));
                    }
                } else if (psiElement instanceof PsiField) {
                    PsiField psiField = (PsiField) psiElement;
                    PsiClass containingClass = PsiUtil.getContainingClass(psiField);
                    if (!sameClass(containingClass) && refExp.getQualifierExpression() == null) {
                        boolean isStatic = psiField.getModifierList().hasModifierProperty("static");
                        PsiElementFactory elementFactory = exp.getManager().getElementFactory();
                        String qualifier = null;
                        if (isStatic) {
                            qualifier = containingClass.getQualifiedName();
                        } else {
                            qualifier = (String) currentParameterExpressions.get("this");
                            if (qualifier == null) {
                                throw new CantInlineException("Blah", exp);
                            }
                        }
                        if (qualifier != null) {
                            PsiExpression statementFromText = elementFactory.createExpressionFromText(qualifier + "." + psiField.getName(), exp);
                            return (PsiExpression) exp.replace(statementFromText);
                        }
                    }
                } else if (psiElement instanceof PsiParameter) {
                    throw new CantInlineException("Caller includes parameter: " + ((PsiParameter) psiElement).getName(), exp);
                }
            } else if (exp instanceof PsiThisExpression) {
                PsiClass containingClass = PsiUtil.getContainingClass(exp);
                if (!sameClass(containingClass)) {
                    throw new CantInlineException("Can only pass 'this' through the same class", exp);
                }
            }
            return exp;
        }
        catch (IncorrectOperationException e) {
            throw new RuntimeException(e);
        }
    }


    private boolean sameClass(PsiClass containingClass) {
        return thisClass.getQualifiedName().equals(containingClass.getQualifiedName());
    }
}
